﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net.Mail;
using MassMailing.WPF.Content;
using MassMailing.WPF.Settings;

namespace MassMailing.WPF.Commands
{
    /// <summary>
    /// A command that performs the entire mass mailing command.
    /// </summary>
    public class MassMailingMasterCommand : BaseCommand
    {
        #region Fields

        public event EventHandler AllMessagesSent;
        public event AsyncCompletedEventHandler MessageSent;
        public event SendCompletedEventHandler SendCompleted;

        private List<Recipient> _recipients;
        private MassMailingMessage _message;
        private List<MassMailingCommand> _mailingCommands;

        #endregion

        #region Constructors

        /// <summary>
        /// Default Constructor for Mass Mailing Command.
        /// </summary>
        /// <param name="recipients">The recipients of the mass mailing.</param>
        /// <param name="message">The message to send to all the recipients.</param>
        public MassMailingMasterCommand(List<Recipient> recipients, MassMailingMessage message)
        {
            _recipients = recipients;
            _message = message;
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// The event handler for the message sent event on the mailing command.
        /// </summary>
        private void MailingCommand_MessageSent(object sender, AsyncCompletedEventArgs e)
        {
            RaiseMessageSent(sender, e);
        }

        /// <summary>
        /// The event handler for the send completed event on the mailing command.
        /// </summary>
        private void MailingCommand_SendCompleted(object sender, AsyncCompletedEventArgs e)
        {
            RaiseSendCompleted(sender, e);
        }

        #endregion

        #region Methods

        /// <summary>
        /// The method that performs the Mass Mailing Master Command.
        /// </summary>
        protected override void PerformCommand()
        {
            MailSettings mailSettings = MailSettings.LoadSettings();
            IEnumerable<Recipient> recipients = _recipients;

            _mailingCommands = new List<MassMailingCommand>();
            List<SMTPSetting> smtpSettings = mailSettings.SMTPSettings;

            while (recipients.Any())
            {
                for (int i = 0; i < smtpSettings.Count; i++)
                {
                    SMTPSetting smtpSetting = smtpSettings[i];
                    // Determine the maximum emails to be able to send.
                    int sendingCount = smtpSetting.MaximumSentEmails ?? recipients.Count();
                    // See if an even distribution between the remaining smtp servers will be less than the maximum
                    sendingCount = Math.Min(sendingCount, (recipients.Count() / (smtpSettings.Count - i)) + 1);
                    if (sendingCount < 1) continue;
                    if (smtpSetting.MaximumSentEmails.HasValue) smtpSetting.MaximumSentEmails -= sendingCount;
                    IEnumerable<Recipient> sendingRecipients = recipients.Take(sendingCount);
                    recipients = recipients.Skip(sendingCount);

                    MassMailingCommand mailingCommand = new MassMailingCommand(smtpSetting, sendingRecipients, _message);
                    mailingCommand.MessageSent += new AsyncCompletedEventHandler(MailingCommand_MessageSent);
                    mailingCommand.SendCompleted += new SendCompletedEventHandler(MailingCommand_SendCompleted);
                    _mailingCommands.Add(mailingCommand);
                    if (!recipients.Any()) break;
                }

                // Check if there is an smtp server with no limit on the emails it can send
                // or if a smtp server has not reached it's limit
                // otherwise discontinue trying to send emails.
                if (!smtpSettings.Exists(smtp => !smtp.MaximumSentEmails.HasValue || smtp.MaximumSentEmails > 0)) break;
            }

            // Throw errors for all the remaining recipients.
            foreach (Recipient recipient in recipients)
            {
                AsyncCompletedEventArgs e = new AsyncCompletedEventArgs(new InvalidOperationException("Ran out of maximum send emails on smtp servers."), false, recipient);
                RaiseSendCompleted(this, e);
            }

            ExecuteCommandsAndWait(_mailingCommands);
            RaiseAllMessagesSent();
        }

        /// <summary>
        /// Raises the all messages sent event.
        /// </summary>
        private void RaiseAllMessagesSent()
        {
            if (AllMessagesSent != null)
            {
                AllMessagesSent(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Raises the message sent event.
        /// </summary>
        /// <param name="sender">The sender that created teh event.</param>
        /// <param name="e">The event args that are associated with this event.</param>
        private void RaiseMessageSent(object sender, AsyncCompletedEventArgs e)
        {
            if (MessageSent != null)
            {
                MessageSent(sender, e);
            }
        }

        /// <summary>
        /// Raises the send completed event.
        /// </summary>
        /// <param name="sender">The sender that created the event.</param>
        /// <param name="e">The event args that are associated with this event.</param>
        private void RaiseSendCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (SendCompleted != null)
            {
                SendCompleted(sender, e);
            }
        }

        #endregion
    }
}
